﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using DataAccess_Layer;

namespace Business_Layer
{
    public enum TypeComponent
    {
        None,
        Body,
        Relation,
        Picture
    }

    public enum Index
    {
        Next,
        Previous
    }

    [Serializable]
    public class Project
    {
        public Project()
        {
            initialize();
            head = mindmaps.ElementAt(0);
            currentMindmap = head;
        }

        private int componentId;
        
        private List<Mindmap> mindmaps;
        private Mindmap head;
        private Mindmap currentMindmap;
        private Presentation presentation;

        private Color currentColor;

        /// <summary>
        /// Initiates a new project.
        /// </summary>
        private void initialize()
        {
            mindmaps = new List<Mindmap>();
            mindmaps.Add(new Mindmap("Main"));
            currentColor = Color.White;
        }

        /// <summary>
        /// Change the current used color.
        /// </summary>
        /// <param name="color">Color object.</param>
        public void ChangeColor(Color color)
        {
            currentColor = color;
        }

        /// <summary>
        /// Changes the current selected mindmap by another mindmap.
        /// </summary>
        /// <param name="id"></param>
        public void ChangeMindmap(int id)
        {
            currentMindmap = mindmaps[id];
        }

        /// <summary>
        /// Adds a new mindmap to the mindmap list.
        /// </summary>
        /// <param name="name">Mindmaps' name.</param>
        public void AddMindmap(string name)
        {
            if (name.Trim() != "")
            {
                bool found = false;
                for (int i = 0; i < mindmaps.Count; i++)
                {
                    if (mindmaps[i].Name.ToLower() == name.Trim().ToLower())
                    {
                        found = true;
                    }
                }
                if (!found)
                {
                    mindmaps.Add(new Mindmap(name));
                }

            }
        }

        /// <summary>
        /// Deletes a mindmap by ID.
        /// </summary>
        /// <param name="id">A mindmaps ID.</param>
        /// <returns></returns>
        public bool DeleteMindmap(int id)
        {
            if (mindmaps.Count < 2 || id == 0)
            {
                return false;
            }
            else
            {
                mindmaps.RemoveAt(id);
                currentMindmap = mindmaps[0];
                return true;
            }
        }

        /// <summary>
        /// Used to add a component to the mindmap
        /// </summary>
        /// <param name="type">The type of the component</param>
        /// <param name="sx">The starting X-coordinate</param>
        /// <param name="sy">The starting Y-coordinate</param>
        /// <param name="ex">The ending X-coordinate</param>
        /// <param name="ey">The ending Y-coordinate</param>
        /// <param name="text">The text to show with the component</param>
        /// <param name="picture">The picture to show. Set null if type component is not a picture!</param>
        public Bitmap AddComponent(TypeComponent type, 
                                   int sx, 
                                   int sy, 
                                   int ex, 
                                   int ey, 
                                   string text = "",
                                   Bitmap picture = null)
        {
            switch (type)
            {
                case TypeComponent.None:
                    break;
                case TypeComponent.Body:
                    currentMindmap.AddBody(sx, sy, ex, ey, text, currentColor);
                    break;
                case TypeComponent.Relation:
                    SelectComponent(sx,sy);
                    int s = componentId;
                    
                    SelectComponent(ex,ey);
                    int e = componentId;

                    if (s != -1 && e != -1)
                    {
                        currentMindmap.AddRelation(currentMindmap.GetComponents[s], currentMindmap.GetComponents[e], text);
                    }
                    break;
                case TypeComponent.Picture:
                    currentMindmap.AddPicture(sx, sy, ex, ey, text, picture);
                    break;
                default:
                    break;
            }

            return currentMindmap.Draw();
        }

        /// <summary>
        /// Select a component by it's coordinates.
        /// </summary>
        /// <param name="x">X Coordinate.</param>
        /// <param name="y">Y Coordinate.</param>
        public void SelectComponent(int x, int y)
        {
            componentId = -1;
            List<Component> components = currentMindmap.GetComponents;

            for (int i = components.Count - 1; i >= 0; i--)
            {
                if (((x > components[i].GetBounds.X && 
                    x < components[i].GetBounds.Width) ||
                    (x < components[i].GetBounds.X &&
                    x > components[i].GetBounds.Width)) &&
                    ((y > components[i].GetBounds.Y &&
                    y < components[i].GetBounds.Height) ||
                    (y < components[i].GetBounds.Y &&
                    y > components[i].GetBounds.Height)))
                {
                    componentId = i;
                }
            }
            Console.WriteLine("Selected: " + componentId);
        }

        /// <summary>
        /// Resets the selection. Sets it to -1.
        /// </summary>
        public void ResetSelection()
        {
            componentId = -1;
        }

        /// <summary>
        /// Gets the text of the current selected component.
        /// </summary>
        /// <returns></returns>
        public string GetComponentsText()
        {
            string text = null;
            if (componentId != -1)
            {
                text = currentMindmap.GetComponents.ElementAt(componentId).GetText();
            }
            return text;
        }

        /// <summary>
        /// Edits the current selected components text.
        /// </summary>
        /// <param name="text">New text.</param>
        public void EditComponentsText(string text)
        {
            if (componentId != -1)
            {
                currentMindmap.GetComponents.ElementAt(componentId).ChangeText(text);
                currentMindmap.Update();
                componentId = -1;
            }
        }

        /// <summary>
        /// Edits the current selected components color.
        /// </summary>
        /// <param name="color">Color object.</param>
        public void EditComponentsColor(Color color)
        {
            if (componentId != -1)
            {
                currentMindmap.GetComponents.ElementAt(componentId).ChangeColor(color);
                currentMindmap.Update();
                componentId = -1;
            }
        }

        /// <summary>
        /// Deletes the current selected component.
        /// </summary>
        public void DeleteSelected()
        {
            DoDelete(componentId);
            componentId = -1;
        }

        /// <summary>
        /// Actually deletes the component. Previous method was to distract the outer world. ;D
        /// </summary>
        /// <param name="id">Components ID.</param>
        private void DoDelete(int id)
        {
            if (id != -1)
            {
                for (int i = 0; i < currentMindmap.GetComponents.Count; i++)
                {
                    if (currentMindmap.GetComponents[i] is Relation)
                    {
                        Relation rel = (Relation)currentMindmap.GetComponents[i];

                        if (currentMindmap.GetComponents[id] == rel.Component1 || currentMindmap.GetComponents[id] == rel.Component2)
                        {
                            DoDelete(i);
                            i--;
                        }
                    }
                }

                try
                {
                    currentMindmap.GetComponents.RemoveAt(id);
                    currentMindmap.Update();
                }
                catch (ArgumentOutOfRangeException ex)
                {
                    Console.WriteLine("Something went wrong with deleting a component. This is the system error message: \r\n" + ex.Message);
                }
                finally
                {
                    id = -1;
                }
            }
        }

        /// <summary>
        /// Moves the component into the specified direction.
        /// </summary>
        /// <param name="dx">Direction X.</param>
        /// <param name="dy">Direction Y.</param>
        public void MoveComponent(int dx, int dy)
        {
            if (componentId != -1)
            {
                try
                {
                    currentMindmap.MoveComponent(componentId, dx, dy);
                }
                catch (ArgumentOutOfRangeException ex)
                {
                    Console.WriteLine("No component was selected or the selected component was out of range. /r/n/r/n" + ex.Message);
                }
            }
        }

        /// <summary>
        /// Adds (and thus creates) a new presentation.
        /// </summary>
        public void AddPresentation()
        {
            presentation = new Presentation();
            foreach (Mindmap m in mindmaps)
            {
                presentation.AddDia(m.GetScreen());
            }
        }

        /// <summary>
        /// Undoes a step.
        /// </summary>
        public void UndoStep()
        {
            currentMindmap.SetStep(Mindmap.StepType.UndoStep);
        }

        /// <summary>
        /// Redoes a step.
        /// </summary>
        public void RedoStep()
        {
            currentMindmap.SetStep(Mindmap.StepType.RedoStep);
        }

        /// <summary>
        /// Returns all mindmap names. Used for explorers etc.
        /// </summary>
        public List<string> MindmapNames
        {
            get
            {
                List<string> names = new List<string>();
                for (int i = 0; i < mindmaps.Count; i++)
                {
                    names.Add(mindmaps[i].Name);
                }
                return names;
            }
        }

        /// <summary>
        /// Preview a component while dragging boundaries on the screen.
        /// </summary>
        /// <param name="typeComponent">Type.</param>
        /// <param name="sx">The X Coordinate to start.</param>
        /// <param name="sy">The Y Coordinate to start.</param>
        /// <param name="ex">The X Coordinate to end.</param>
        /// <param name="ey">The Y Coordinate to end.</param>
        /// <returns></returns>
        public Bitmap PreviewComponent(TypeComponent typeComponent, int sx, int sy, int ex, int ey)
        {
            return currentMindmap.PreviewComponent(typeComponent, sx, sy, ex, ey, currentColor);
        }

        /// <summary>
        /// Returns a components' alternate action.
        /// </summary>
        /// <returns></returns>
        public string AlternateAction()
        {
            if (componentId == -1)
                return null;

            string alternate = currentMindmap.GetComponents[componentId].AlternateString;

            if (alternate == null)
                return null;

            if (alternate.ToLower().StartsWith("http:"))
            {
                if (alternate.ToLower().IndexOf("http:", 5) != -1)
                {
                    alternate = alternate.Remove(0, 5);
                }
                System.Diagnostics.Process.Start(alternate);
            }
            else if (alternate.ToLower().StartsWith("mindmap:"))
            {
                alternate = alternate.Remove(0, 8);
                return alternate;
            }
            else if (alternate.ToLower().StartsWith("file:"))
            {
                //alternate = alternate.Remove(0, 5);
                System.Diagnostics.Process.Start(alternate);
            }
            return null;
        }

        /// <summary>
        /// Returns the current mindmap.
        /// </summary>
        /// <returns></returns>
        public Bitmap ShowMindmap()
        {
            return currentMindmap.Draw();
        }

        /// <summary>
        /// Gets the initial presentation dia.
        /// </summary>
        /// <param name="screen">Screen to draw on.</param>
        /// <returns></returns>
        public Bitmap GetInitialDia(Bitmap screen)
        {
            screen = presentation.GetCurrentDia(screen);
            return screen;
        }

        /// <summary>
        /// Gets a dia by index.
        /// </summary>
        /// <param name="screen">Screen to draw on.</param>
        /// <param name="index">Index for direction to browse.</param>
        /// <returns></returns>
        public Bitmap GetDia(Bitmap screen, Index index)
        {
            switch (index)
            {
                case Index.Next:
                    presentation.NextDia();
                    screen = presentation.GetCurrentDia(screen);
                    break;
                case Index.Previous:
                    presentation.PreviousDia();
                    screen = presentation.GetCurrentDia(screen);
                    break;
                default:
                    break;
            }
            return screen;
        }

        /// <summary>
        /// Returns a list with all dias.
        /// </summary>
        /// <returns></returns>
        public List<Dia> GetDias()
        {
            return presentation.GetDias();
        }

        /// <summary>
        /// Moves a dia into a certain direction.
        /// </summary>
        /// <param name="selected">Dia to move.</param>
        /// <param name="direction">Direction to move in. (Can either be 1 or -1).</param>
        public void ModifyDia(int selected, int direction)
        {
            if (selected + direction < presentation.GetDias().Count &&
                selected - direction > 0)
            {
                Dia tempSelected = presentation.GetDias().ElementAt(selected);
                Dia tempDirected = presentation.GetDias().ElementAt(selected + direction);
                List<Dia> dias = new List<Dia>(presentation.GetDias());
                dias[selected] = tempDirected;
                dias[selected + direction] = tempSelected;
                presentation.SetDias(dias);
            }
        }

        /// <summary>
        /// Add an alternate function to a component.
        /// </summary>
        /// <param name="argument"></param>
        public void AddAlternate(string argument)
        {
            if (componentId == -1)
                return;
            currentMindmap.GetComponents[componentId].AlternateString = argument;
        }

        /// <summary>
        /// Save this project to a binary file.
        /// </summary>
        /// <param name="path"></param>
        public void Save(string path)
        {
            FileWriter file = new FileWriter();
            file.Write(this, path);
        }

        /// <summary>
        /// Save this project to HTML files.
        /// </summary>
        /// <param name="template">The template HTML file.</param>
        /// <param name="path">The path too save to.</param>
        public void SaveAsHtml(string path, string template)
        {
            string[] names = new string[mindmaps.Count];
            for (int i = 0; i < mindmaps.Count; i++)
            {
                names[i] = mindmaps[i].Name;
            }

            for (int i = 0; i < mindmaps.Count; i++)
            {
                string a = template.Replace("?0?", names[i]);
                string b = a.Replace("?1?", names[i] + ".bmp");
                string c = null;
                string d = null;
                if (i - 1 >= 0) c = b.Replace("?2?", names[i - 1] + ".html");
                else c = b.Replace("?2?", "");
                if (i + 1 < mindmaps.Count) d = c.Replace("?3?", names[i + 1] + ".html");
                else d = c.Replace("?3?", "");

                FileWriter file = new FileWriter();
                file.WriteAsHtml(path, names[i], d, mindmaps.ElementAt(i).GetScreen());
            }
        }

        /// <summary>
        /// Opens a new file into this instance.
        /// </summary>
        /// <param name="mindmaps">A list with mindmaps.</param>
        /// <param name="currentMindmap">The start mindmap.</param>
        /// <param name="head">The head mindmap.</param>
        public void Open(List<Mindmap> mindmaps,
                         Mindmap currentMindmap,
                         Mindmap head)
        {
            this.mindmaps = mindmaps;
            this.currentMindmap = currentMindmap;
            this.head = head;
        }

        /// <summary>
        /// Returns the headmindmap. Used for serialization.
        /// </summary>
        /// <returns></returns>
        public Mindmap GetHeadMindmap()
        {
            return head;
        }

        /// <summary>
        /// Returns the current mindmap. Used for serialization.
        /// </summary>
        /// <returns></returns>
        public Mindmap GetCurrentMindmap()
        {
            return currentMindmap;
        }

        /// <summary>
        /// Returns the mindmap list. Used for serialization.
        /// </summary>
        /// <returns></returns>
        public List<Mindmap> GetMindmaps()
        {
            return mindmaps;
        }
    }
}
